#pragma once
#include "index.hpp"
#include "util.hpp"
#include <algorithm>
#include <jsoncpp/json/json.h>
namespace ns_search
{
  struct InvertedElemPrint
  {
        // 文档ID
    std::uint64_t doc_id;
    // 关键字
    std::vector<std::string> words;
    // 权重
    int weight;
  };
  class Searcher
  {
  public:
    Searcher() {}
    ~Searcher() {}
    // 初始化搜索函数
    void IintSeacher(const std::string &input)
    {
      // 1.获取或创立index对象
      index = ns_index::Index::GetIndex();
      std::cout << "构建index单例成功" << std::endl;
      // 2.根据index对象及出入字符串 构建索引 build...
      index->BuildIndex(input);
      std::cout << "构建正排与倒排索引成功" << std::endl;
    }
    void Seach(const std::string &query, std::string *json_string)
    {
      // 1.[分词] 根据query进行分词 eg:篮球传奇 -- 篮球 传奇 --jieba分词
      std::vector<std::string> words; // 存储query的分词
      ns_util::JiebaUtil::CutString(query, &words);
      // 2.[查找] 根据分好的词进行倒排查找 要忽略大小写 分词后统一用小写
      //  // 通过关键字获得倒排拉链 InvertedList *GetInverted(const std::string &word)
      //ns_index::InvertedList invertedlist_all; // 用来存放所有关键字所对应的文档 会出现重复情况 文档1 文档2 文档1 文档2 .....TODO 见下
      std::vector<InvertedElemPrint> invertedlist_all;
      std::unordered_map<uint64_t,InvertedElemPrint> tokens_map;//用来对invertedlist_all  去重
      //eg 王虎是对的 王虎/是/对/的 words=王虎,是 
      for (std::string &word : words)
      {
        boost::to_lower(word);
        ns_index::InvertedList *inverted_list = index->GetInverted(word); // 用来存放单个关键字所对应文档
        if (nullptr == inverted_list)
          continue;
        for(const auto &elm: *inverted_list)
        {
          auto & item=tokens_map[elm.doc_id];//item是InvertedElemPrint
          item.doc_id = elm.doc_id;
          item.weight +=elm.weight;
          item.words.push_back(elm.word);
        }
        //item{
        //  weight=2;doc_id=xx;words=王虎,是
        //}
        //invertedlist_all.insert(invertedlist_all.end(), inverted_list->begin(), inverted_list->end());
      }
        for(auto &item:tokens_map)
        {
          invertedlist_all.push_back(item.second);
        }
      // 3.[合并排序] 根据weight对查找到的内容进行降序排序
      //std::sort(invertedlist_all.begin(), invertedlist_all.end(), [](const ns_index::InvertedElem &e1, const ns_index::InvertedElem &e2)
                //{ return e1.weight > e2.weight; });
      std::sort(invertedlist_all.begin(), invertedlist_all.end(), [](const InvertedElemPrint & e1,const InvertedElemPrint e2 ){
        return e1.weight>e2.weight;
      });
      // 4.[输出] 把查找到的文档以 json字符串的形式输出给浏览器
      Json::Value root;
      for (auto &item : invertedlist_all)
      {
        ns_index::DocInfo *doc = index->GetForwardIndex(item.doc_id);
        if (nullptr == doc)
          continue;
        Json::Value elm;
        elm["title"] = doc->title;
        elm["desc"] = GetDesc(doc->content,item.words[0]); // todo这里要的只是提取出html页面内容的摘要
        elm["url"] = doc->url;
        elm["id"] = doc->doc_id;
        elm["wight"] = item.weight;
        root.append(elm);
      }
      Json::StyledWriter writer;
      *json_string = writer.write(root);
    }

    // 根据关键字对html页面的内容进行提取 找到第一个关键字出现的位置然后向前截取50个字节 向后截取100个字节
    std::string GetDesc(const std::string &html_content, std::string &word)
    {
      // word是经过小写化处理的字符串 但是html_content里的字符没有经过处理 所以会存在不匹配问题
      // std::size_t pos = html_content.find(word);
      auto iter = std::search(html_content.begin(), html_content.end(), word.begin(), word.end(), [](int x, int y)
                              { return std::tolower(x) == std::tolower(y); });
      if (iter == html_content.end())
        return "NONE";
      int prev_step = 50;
      int next_step = 100;
      int start = 0;
      int end = html_content.size() - 1;
      int pos = std::distance(html_content.begin(),iter);
      // size_t 是无符号数 这一步会出现问题pos-prev_step>start
      if (pos > start + prev_step)
        start = pos - start;
      if (pos + next_step < end)
        end = pos + next_step;
      if (start > end)
        return "NONE2";
      return html_content.substr(start, end - start);
    }

  private:
    ns_index::Index *index; // 供系统查找的索引
  };
}